package net.htjs.jdkgrammer.jdk19;

import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

@Slf4j
public class VirtualThreadTest {

    /**
     * 平台线程（platform thread）：指java中的线程，比如通过Executors.newFixedThreadPool()创建出来的线程，我们称之为平台线程。
     * 虚拟线程并不会直接分配给cpu去执行，而是通过调度器分配给平台线程，平台线程再被调度器管理。java中虚拟线程的调度器采用了工作窃取的模式进行FIFO的操作，
     * 调度器的并行数默认是jvm获取的处理器数量（通过该方法获取的数量Runtime.getRuntime().availableProcessors()），
     * 调度器并非分时（time sharing）的。在使用虚拟线程编写程序时，不能控制虚拟线程何时分配给平台线程，也不能控制平台线程何时分配给cpu。
     *
     * 携带器
     * 调度器将虚拟线程挂载到平台线程之后，该平台线程叫做虚拟线程的携带器，调度器并不维护虚拟线程和携带器之间的关联关系，
     * 因此在一个虚拟线程的生命周期中可以被分配到不同的携带器，即虚拟线程运行了一小段代码后，可能会脱离携带器，此时其他的虚拟线程会被分配到这个携带器上。
     *
     * 携带器和虚拟线程是相互独立的，比如：
     *
     * 虚拟线程不能使用携带器的标识，Thread.current()方法获取的是虚拟线程本身。
     * 两者有各自的栈空间。
     * 两者不能访问对方的Thread Local变量。
     * 在程序的执行过程中，虚拟线程遇到阻塞的操作时大部分情况下会被解除挂载，阻塞结束后，虚拟线程会被调度器重新挂载到携带器上，
     * 因此虚拟线程会频繁的挂载和解除挂载，这并不会导致操作系统线程的阻塞。下面的代码在执行两个get方法和send方法（会有io操作）
     * 时会使虚拟线程发生挂载和解除挂载：
     * @param args
     */
    public static void main(String[] args) {
        //newVirtualThreadPerTaskExecutor为每个任务创建一个虚拟线程
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 1000_000).forEach(i -> {
                executor.submit(() -> {
                    try {
                        //睡眠1秒，模拟耗时操作
                        Thread.sleep(Duration.ofSeconds(1));
                        log.info("执行任务:" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            });
        }

    }

}
